
/* BOI API v2.0 *********************************/
/* Project Name: Business Objects Integration	*/
/* Developed by: Ali H. Al-Towailib				*/
/*                                  			*/
/* **********************************************/
/* Important: This API Requires initialization of jQuery first.
/*
 * This API can be used by referencing BOI singleton instance. e.g. BOI.openDocument(...)
 * The instance itself must reside on the HTML Framework.
 * Singleton instance will be automatically initialised on script inclusion in any HTML document
 */

/********************************* BOI Object Oriented Class Library  ********************************************/
var BOILib =
{
    //Constants and Lookup enums
    VariableTypes:
    {
        Number: 'number',
        String: 'string',
        Date: 'date',
        DateTime: 'datetime',
        Time: 'time',
        Boolean: 'boolean'
    },
    F4Modes:
    {
        QueryDef: 'QUERY_DEF',
        BWReport: 'BW_REPORT'
    },

    SelectionTypes:
    {
        Single: 'S',
        Multiple: 'M',
        Range: 'R',
        Complex: 'C'
    },

    OutputTypes:
    {
        XML: 'xml',
        JSON: 'json'
    },
    
    ReportTypes:
    {
    	Bex3: 'bex3',
    	Bex7: 'bex7',
    	Webi: 'webi',
    	Xcelsius: 'xcelsius',
    	XcelsiusLocal: 'xcelsiuslocal',
    	Crystal: 'crystal',
    	Explorer: 'explorer',
    	Analysis: 'analysis',
    	URL: 'url',
    	BO: 'BO'
    },

    ReportStates:
    {
        Current: "current", //report has current variable values
        RefreshNeeded: "refresh" //report has obsolete variable values and needs to be reopened
    },

    Report: function Report()
    {
        this.role = "REPORTROLE";
        this.id = "report id";
        this.url = "http://myreporturl.com/"; //Report URL
        this.tech_name = "My Report TECHNAME";
        this.label = "My Report Label";

        this.state = BOILib.ReportStates.RefreshNeeded;
        this.type = "BO"; //Report Type (bex3,bex7,webi,crystal,xcelsius,explorer,analysis)
        this.layout = null; //report layout info
        this.vars = ""; //string representing variables
        this.variables = {}; //Variable objects

        this.metadata = null;

        this.getInfo = function ()
        {
            var output = '';
            var varcount = 0;

            for (var i in this.variables)
            {
                varcount++;
                output += '\n\t\t' + this.variables[i].getInfo();
            }

            return  '\n\t----- BEGIN CURRENT REPORT INFORMATION -----------------------------------------' +
            	'\n\tReport Id: ' + this.id +
                '\n\tReport Tech Name: ' + this.tech_name +
                '\n\tReport URL: ' + this.url +
                '\n\tVariables Count: ' +  varcount +
                '\n\tVariables Stored:' + output +
                '\n\tVariables Original String: ' + this.vars +
                '\n\t----- END OF CURRENT REPORT INFORMATION -----------------------------------------';
        };
    },

    Range: function Range(fromMode,from,to,toMode,operator)
    {
        this.operator = operator ? operator : "bt";//possible operators using abap like syntax : bt,gt,ge,lt,le,eq
        this.fromMode = fromMode ? fromMode : "["; //default to inclusion using BO OpenDoc Syntax
        this.toMode = toMode ? toMode : "]"; //default to inclusion, use ( for excluding value
        this.from = from ? from : "";
        this.to = to ? to : "";
    },

    Variable: function Variable ()
    {
        this.name = ""; //ZVARNAME
        this.description = ""; //ZVAR_DESCRIPTION
        this.type = BOILib.VariableTypes.Number; //date,datetime,time,boolean,number,string
        this.seltype = BOILib.SelectionTypes.Single; //variable selection type
        this.f4type = BOILib.F4Modes.QueryDef; //or BW_REPORT
        this.f4query = ""; //query to execute in order to get F4 values
        this.defval = ""; //DEF_VAL
        this.value = undefined; //current value, can be date object
        this.values = []; //for multi value variable (if type is date, contains date objects)
        this.ranges = []; //for single or multi range variable (contains Range objects)
        this.dependencies = {}; //what vars this variable depends on

        this.zdisplay = ""; //VIEW_ID: CHECK_BOX, etc


        this.f4url = ""; //url to get f4 values from
        this.F4 = {}; //possible F4 selection values (lookup, predefined values)
        this.infoobject = null;

        this.isReady = function() //determines if this variable is ready for use (i.e. dependencies must have values)
        {
            for(var i in this.dependencies)
                if(this.dependencies[i].value == undefined)
                    return false;
            return true;
        };

        this.getInfo = function() 
        {

            var f4count = 0;
            for(property in this.F4) f4count++;
            
            var vals = '';
            if(this.seltype == BOILib.SelectionTypes.Multiple)
            	for(var i in this.values)
            		vals+= this.values[i] + "|";

            return this.name + ' (' + this.description + '), Value:' + this.value + ', F4:' + f4count + ', MultiValues:' + vals;
        };
    },

    F4: function F4 ()
    {
        this.name = "My F4 Entry"; //F4 Entry Name
        this.value = "1"; //F4 Entry Value

        this.getInfo = function() {
            return this.name + ':' + this.value;
        };
    },

    F4Set: function F4Set ()
    {
        this.varname = "My Variable"; //F4Set Entry Name
        this.values = {}; //F4 Objects
        this.cycle = 1; //start index
        this.records = 100; //page size
        this.total = 200; //total records available
        this.url = "";

        this.getInfo = function() {
            return this.name + ':' + this.value;
        };
    },

    Uri: function Uri(fullurl)
    {
    	this.url = ""; //url without query string
    	this.fullUrl = ""; //Complete url
    	this.params = {}; //param['var1']=4545, param['var2']=34554, etc.. key value pairs
    	
    	if(fullurl)
    	{
    		this.url = fullurl.split('?')[0];
    		this.fullUrl = fullurl;
            if(fullurl.indexOf('?') > -1)
            {
                var querystring = fullurl.split('?')[1];
                if(querystring.indexOf('&') > -1)
                {
                    var parameters = querystring.split('&');
                    for(var i in parameters)
                    {
                        if(parameters[i].indexOf('=')>-1)
                        {
                            var block = parameters[i].split('=');
                            this.params[block[0]] = block[1];
                        }
                    }
                }
            }
    	}
    	
    	this.post = function(target)
    	{
    		target = typeof target !== 'undefined' ? target : '_blank'; //use defaults if not specified
    		
    		//create an iframe
    		//create a form
    		//put the params in hiddenfields
    		//post the form to the target
    		
    		var form = document.createElement("form");
    	    form.setAttribute("method", 'post');
    	    form.setAttribute("action", this.url);
    	    form.setAttribute("target", target);


    	    for(var key in this.params) 
    	    {
    	        if(this.params.hasOwnProperty(key)) 
    	        {
    	            var hiddenField = document.createElement("input");
    	            hiddenField.setAttribute("type", "hidden");
    	            hiddenField.setAttribute("name", key);
    	            hiddenField.setAttribute("value", this.params[key]);
    	            form.appendChild(hiddenField);
    	         }
    	    }

    	    document.body.appendChild(form);
    	    form.submit();

    	    document.body.removeChild(form);
    		
    	    return this.fullUrl;
    	};

        this.get = function(target)
        {
            target = typeof target !== 'undefined' ? target : '_blank'; //use defaults if not specified

            //create an iframe
            //create a form
            //put the params in hiddenfields
            //post the form to the target

            var form = document.createElement("form");
            form.setAttribute("method", 'get');
            form.setAttribute("action", this.url);
            form.setAttribute("target", target);


            for(var key in this.params)
            {
                if(this.params.hasOwnProperty(key))
                {
                    var hiddenField = document.createElement("input");
                    hiddenField.setAttribute("type", "hidden");
                    hiddenField.setAttribute("name", key);
                    hiddenField.setAttribute("value", this.params[key]);
                    form.appendChild(hiddenField);
                }
            }

            document.body.appendChild(form);
            form.submit();

            document.body.removeChild(form);

            return this.fullUrl;
        };

        this.ajaxPost = function(successFunction)
        {
            var params = {};
            //decode any stuff
            for(var key in this.params)
                params[key] = decodeURIComponent(this.params[key]);

            $.ajax({
                url: this.url,
                data: params,
                type: 'POST',
                dataType: 'text',
                success: function(data)
                {
                    if(successFunction)
                        successFunction(data);
                }
            });
        }
    },
    
    Context: function Context ()
    {
        this.parent = BOI;
        this.lastReport = new BOILib.Report(); //last opened report
        this.lastUrl = "";
        this.reports = {}; //array of reports from catalog
        this.vars = ""; //string representing last variables passed
        this.variables = {}; //Array of Current Variable objects
        this.reportCatalog = null; //xml
        this.reportCatalogJSON = null; //json
        this.reportCatalogTree = null; //json tree with isFolder attributes

        this.populateCatalog = function() //creates initial report objects
        {
            var count = 0;
            var context = this;
            $(this.reportCatalog).find('*[tech_name][isBranch="false"]').each(
                function()
                {
                    var report = new BOILib.Report();
                    report.role = $(this).attr("role");
                    report.id = $(this).attr("id");
                    report.url = $(this).attr("url");
                    report.tech_name = $(this).attr("tech_name");
                    report.type = $(this).attr("type");
                    report.label = $(this).attr("label");
                    context.reports[report.id] = report;
                    count++;
                }
            );
            if(parent.debugMode) BOI.log += BOILib.Util.log('Done Populating Context with ' + count + ' reports');
        };
        this.getInfo = function()
        {
            var output = '';
            var varcount = 0;

            for (var i in this.variables)
            {
                varcount++;
                output += '\n\t' + this.variables[i].getInfo();
            }

            return  '\n----- BEGIN CONTEXT INFORMATION -----------------------------------------' +
            	'\nLast Report id: ' + this.lastReport.id +
            	'\nLast Report TechName: ' + this.lastReport.tech_name +
                '\nLast Generated Report URL: ' + this.lastUrl +
                '\nContext Variables Count: ' +  varcount +
                '\nListing of Variables Stored:' + output +
                '\nVariables Original String(if provided): ' + this.vars +
                '\nLast Report Info: ' + this.lastReport.getInfo() +
                '\n----- END OF CONTEXT INFORMATION -----------------------------------------';
        };

        this.generateReportUri = function(report, reportVarsOnly) //returns a URI object
        {
        	var url = '';
        	var id = report.id;
            var type = report.type;

        	switch(report.type) //check the type
    		{
    			case BOILib.ReportTypes.Bex3:
    				url = this.generateBex3URL(id, type, reportVarsOnly);
                    url = BOI.urlForBex3 + url;
    				break;
    			case BOILib.ReportTypes.Bex7:
    				url = this.generateBex7URL(id, type, reportVarsOnly);
                    url = BOI.urlForBex7 + url;
                    break;
    			case BOILib.ReportTypes.BO:
    			case BOILib.ReportTypes.Webi:
    			case BOILib.ReportTypes.Crystal:
    				url = this.generateOpenDocumentURL(id, type, reportVarsOnly);

                    //handle refresh
                    if(this.reports[id].state == BOILib.ReportStates.RefreshNeeded)
                        url = "&sRefresh=Y" + url;
                    else
                        url = "&sRefresh=N" + url;
                    url = BOI.urlForOpenDocument + report.id + url;
                    url = "http://" + BOI.urlForServer + "/" + url;
    				break;
                case BOILib.ReportTypes.Xcelsius:
                case BOILib.ReportTypes.XcelsiusLocal:
                    url = this.generateXcelsiusURL(id, type, reportVarsOnly);
                    break;
    			case BOILib.ReportTypes.Explorer:
    			case BOILib.ReportTypes.Analysis:
    				url = this.generateOpenDocumentURL(id, type, reportVarsOnly);
                    url = BOI.urlForOpenDocument + report.id + url;
    				break;
    		}


        	var Uri = new BOILib.Uri(url);
        	this.lastUrl = Uri.fullUrl;
        	return Uri; //returns a URI object
        };

        this.generateBex3URL = function(id, type, reportVarsOnly)
        {
        	var variables = [];
        	if(reportVarsOnly)
        	{
        		for(var i in this.reports[id].variables)
        		{
        			var varname = this.reports[id].variables[i].name;
        			variables.push(this.variables[varname]);
        		}
        	}
        	else
        	{
        		for(var i in this.variables)
        		{
        			variables.push(this.variables[i]);
        		}
        	}

        	//now we have all the variables
        	//begin bex3 logic here...
        	var url = BOILib.BexSyntax.getBex3URL(this.reports[id].tech_name, variables);
        	return url;

        };
        this.generateBex7URL = function(id, type, reportVarsOnly)
        {
        	var variables = [];
        	if(reportVarsOnly)
        	{
        		for(var i in this.reports[id].variables)
        		{
        			var varname = this.reports[id].variables[i].name;
        			variables.push(this.variables[varname]);
        		}
        	}
        	else
        	{
        		for(var i in this.variables)
        		{
        			variables.push(this.variables[i]);
        		}
        	}

        	//now we have all the variables
        	//begin bex3 logic here...
        	var url = BOILib.BexSyntax.getBex7URL(this.reports[id].tech_name, variables);
        	return url;
        };
        this.generateOpenDocumentURL = function(id, type, reportVarsOnly)
        {
        	var variables = [];

        	if(reportVarsOnly)
        	{
        		for(var i in this.reports[id].variables)
        		{
        			var varname = this.reports[id].variables[i].name;
        			variables.push(this.variables[varname]);
        		}
        	}
        	else
        	{
        		for(var i in this.variables)
        		{
        			variables.push(this.variables[i]);
        		}
        	}

            var output = "";



        	//now we have all the variables
        	//begin openDocument logic here...

        	if(variables.length>0)
        	{
	        	for(var i in variables)
	        	{
	        		output += BOILib.OpenDocumentSyntax.getVariableValue(variables[i], type);
	        	}
        	}
        	return output;
        };
        this.generateXcelsiusURL = function(id, type, reportVarsOnly)
        {
            var variables = [];

            if(reportVarsOnly)
            {
                for(var i in this.reports[id].variables)
                {
                    var varname = this.reports[id].variables[i].name;
                    variables.push(this.variables[varname]);
                }
            }
            else
            {
                for(var i in this.variables)
                {
                    variables.push(this.variables[i]);
                }
            }

            var output = "";



            //now we have all the variables
            //begin url syntax logic here: file.swf?varname=value&var2=val2&...

            if(variables.length>0)
            {
                var isLocal = type == BOILib.ReportTypes.XcelsiusLocal;
                for(var i in variables)
                {
                    var val = variables[i].value;
                    if(variables[i].type == BOILib.VariableTypes.Date)
                    {
                        var m = val.getMonth() + 1;
                        var d = val.getDate();
                        var y = val.getFullYear();

                        if(m<10) m = "0" + m;
                        if(d<10) d = "0" + d;
                        val = m + '' + d + '' + y;
                    }

                    if(isLocal)
                        output += '&' + variables[i].name + '=' + val;
                    else
                        output += '&lsS' + variables[i].name + '=' + val;
                }
            }
            if(isLocal)
                return '?' + output.substr(1);
            else
                return output;
        };
    },

    OpenDocumentSyntax:
    {
    	getVariableValue: function(variable, reporttype) //single,multiple,range
    	{
            var type = reporttype;//syntax is different based on type: webi, crystal, etc
    		switch(variable.seltype)
    		{
    			case BOILib.SelectionTypes.Single:
    				return this.getSingleSyntax(variable, type);
    				break;
    			case BOILib.SelectionTypes.Multiple:
    				return this.getMultipleSyntax(variable, type);
    				break;
    			case BOILib.SelectionTypes.Range:
    				return this.getRangeSyntax(variable, type);
    				break;
    		}
    	},

    	getValueFormat: function(variable, valueEntry) //formats the supplied single value according to variabletype
    	{
    		//returns ? (WEBI only: forces prompt to user to enter value)
            //returns Paris (text)
            //returns 2004 (number)
            //returns Date(2004,1,24) (date)
            //returns DateTime(2004,1,24,6,1,1) (datetime)
            //returns Time(6,10,1) (time)
            //returns false (boolean)

    		switch(variable.type)
            {
            	//check type
    			case BOILib.VariableTypes.Number:
    			case BOILib.VariableTypes.String:
    				return valueEntry;
    				break;
    			case BOILib.VariableTypes.Date: //supplied value must be of type Date()
    				return 'Date(' + valueEntry.getFullYear() + ',' + (valueEntry.getMonth() + 1) + ',' + valueEntry.getDate()  + ')';
    				break;
    			case BOILib.VariableTypes.DateTime:
    			case BOILib.VariableTypes.Time:
    				return 'DateTime(' + valueEntry.getFullYear() + ','
    									+ (valueEntry.getMonth() + 1) + ','
    									+ (valueEntry.getDate()) + ','
    									+ (valueEntry.getHours()) + ','
    									+ (valueEntry.getMinutes()) + ','
    									+ valueEntry.getSeconds()  + ')';

    				break;
    			case BOILib.VariableTypes.Boolean: //X=TRUE, blank = false
    				return valueEntry;
    				break;
            }
    	},

        getMultipleSyntax: function(variable, type)
        {
            //returns lsM[Select+Cities]=[Paris],[London],[etc...] for Crystal
            //returns lsMSelect+Cities=Paris;London;etc... for Webi
            //the multiple values can have formats like
            //date,datetime,time,boolean,number,etc..
            //similar to the formats in examples below
    		var output = '';
            var encStart = "[";
            var encEnd = "]";
            var separator = ","; //comma for crystal, for webi use ;

            if(type != BOILib.ReportTypes.Crystal)
            {
                encStart = "";
                encEnd = "";
                separator = ";";
            }

    		for(var i in variable.values)
    		{
    			output+= encStart + this.getValueFormat(variable, variable.values[i]) + encEnd + separator;
    		}
    		if(output.indexOf(separator)>-1)
                output = output.substr(0,output.length - 1); //remove last extra separator

    		return '&lsM' + variable.name + '=' + output;
        },

        getRangeSyntax: function(variable, type)
        {
            //returns lsRTime+Period=[a..d] (set text range)
            //returns lsRTime+Period=[2004..) (set number range start only)
            //returns lsRTime+Period=(..2004] (set number range end only)
            //returns lsRTime+Period=[2004..2009] (set number range)
            //returns lsRTime+Period=[Date(2004,1,24)..Date(2009,2,23)] (set date range)
            //returns lsRTime+Period=[DateTime(2004,1,24,7,1,1)..DateTime(2009,2,23,12,1,1)] (set datetime range)
            //returns lsRTime+Period=[Time(7,1,1)..Time(12,1,1)] (set time range)

            var encStart = "[";
            var encEnd = "]";
            var separator = ","; //comma for crystal, for webi use ;
            var fromMode = variable.ranges[0].fromMode;
            var toMode = variable.ranges[0].toMode;

            if(type != BOILib.ReportTypes.Crystal)
            {
                encStart = "";
                encEnd = "";
                separator = ";";
                fromMode = "";
                toMode = "";
            }

        	return '&lsR' + variable.name + '=' + fromMode + this.getValueFormat(variable, variable.ranges[0].from) + '..'
        												+ this.getValueFormat(variable, variable.ranges[0].to) + toMode;

        },

        getSingleSyntax: function(variable, type)
        {
            //returns lsSSelect+City=? (WEBI only: forces prompt to user to enter value)
            //returns lsSSelect+City=Paris (text)
            //returns lsSSelect+Year=2004 (number)
            //returns lsSSelect+Date=Date(2004,1,24) (date)
            //returns lsSSelect+Date=DateTime(2004,1,24,6,1,1) (datetime)
            //returns lsSSelect+Date=Time(6,10,1) (time)
            //returns lsSBoolean+Choice=false (boolean)

            var encStart = "[";
            var encEnd = "]";
            var separator = ","; //comma for crystal, for webi use ;

            if(type != BOILib.ReportTypes.Crystal)
            {
                encStart = "";
                encEnd = "";
                separator = ";";
            }

            return '&lsS' + variable.name + '=' + this.getValueFormat(variable, variable.value);
        }
    },

    BexSyntax:
    {
    	getBex3URL: function (query_name, variables) //tech_name = report or query name, variables is an optional array of Variable objects
    	{
    		var url ="";
    	    var cnt = 0;
    	    for(var i in variables)
    	    {
    	        var name = variables[i].name;
    	        var value = variables[i].value;
    	        var values = variables[i].values;
    	        var ranges = variables[i].ranges;
    	        var type = variables[i].type; //date,datetime,time,boolean,number,string
    	        var selectionType = variables[i].seltype; //S=Single,M=Multiple,R=Range

    	        cnt ++;

    	        var date_str = "",
    	            range_str = {"from":"","to":"","operator":""};


    	        if(selectionType == 'S')
    	        {
    	            //= value //in case not date
    	            //For Date:check js date object reference for methods and return types..
    	            if(type == 'date')
    	                value = this.dateToString(value);

    	            url += this.Bex3VarFormat( name, cnt ) + this.Bex3ValFormat( value, cnt );

    	        }

    	        if(selectionType == 'M')
    	        {
    	            for(var x in values)
    	            {
    	                cnt += parseInt(x);
    	                var myvalue = values[x];

    	                if(type == 'date')
    	                    myvalue = this.dateToString(myvalue);

    	                url += this.Bex3VarFormat( name, cnt ) + this.Bex3ValFormat( myvalue, cnt );

    	                //in case of data type is date
    	                //For Date:check js date object reference for methods and return types..
    	                //myvalue.getDate() //returns day
    	            }
    	        }

    	        if(selectionType == 'R')
    	        {
    	            for(var r in ranges)
    	            {
    	                var myrange = ranges[r];
    	                if(type == 'date'){

    	                    range_str.from =  this.dateToString( myrange.from );
    	                    range_str.to =  this.dateToString( myrange.to );
    	                    range_str.operator = myrange.operator;
    	                    myrange = range_str;

    	                }
    	                //myrange.operator
    	                //myrange.from
    	                //myrange.to...
    	                //For Date:check js date object reference for methods and return types..

    	                cnt += parseInt(r);

    	                url += this.Bex3VarFormat( name, cnt ) + this.Bex3RangeValFormat(myrange , cnt);

    	            }
    	        }

    	    }

    	    var fullURL = query_name;//'/sap/bw/BEx?=&VARIABLE_SCREEN=&QUERY='+ query_name + '&var....' ;
    	    return fullURL + encodeURIComponent(url);
    	},

    	Bex3VarFormat: function (varName, n)
    	{
    		return  '&var_name_' + n + '=' + varName;
    	},

    	Bex3ValFormat: function (varValue, n)
    	{
    		return  '&var_value_ext_' + n + '=' + varValue +
    	            '&var_operator_' + n + '=EQ' +
    	            '&var_value_low_ext_' + n + '=' + varValue ;
    	},

    	Bex3RangeValFormat: function (range, n)
    	{
    	    return  '&var_operator_'   + n + '=' + range.operator +
    	            '&var_value_low_ext_'  + n + '=' + range.from +
    	            '&var_value_high_ext_' + n + '=' + range.to +
    	            '&var_value_ext_' + n + '=' + range.from;

    	},

    	/*
    	    convert a date obj to string MMDDYYYY
    	*/
    	dateToString: function (date)
    	{
    	    if(date.getUTCMonth && date.getDate && date.getFullYear){
    	        var month = date.getUTCMonth() > 8?  date.getUTCMonth() + 1: '0' +  date.getUTCMonth() + 1 ;
    	        var day   = date.getDate() > 9?  date.getDate() : '0' + date.getDate();
    	        var year  = date.getFullYear();

    	        return month + day + year;
    	    }

    	},



    	/* BEX7  */

    	getBex7URL: function (tech_name, variables)
    	{

    	    var url = "query=" + tech_name;
    	    var cnt = 0;
    	    for(var i in variables)
    	    {
    	        var name = variables[i].name;
    	        var value = variables[i].value;
    	        var values = variables[i].values;
    	        var ranges = variables[i].ranges;
    	        var type = variables[i].type; //date,datetime,time,boolean,number,string
    	        var selectionType = variables[i].seltype; //S=Single,M=Multiple,R=Range

    	        cnt += 1;

    	        if(selectionType == 'S')
    	        {
    	            //= value //in case not date
    	            //For Date:check js date object reference for methods and return types..
    	            if(type == 'date')
    	                value = this.dateToString(value);

    	            url += this.Bex7StrFormat(name, cnt, value);

    	        }

    	        if(selectionType == 'M')
    	        {

    	            var myvalues = "";

    	            if(type == 'date'){
    	                for(var x in values){
    	                    var myvalue = values[x];
    	                    myvalues += this.dateToString(myvalue) + ';';
    	                }

    	            }
    	            else{
    	                myvalues = values.join(';');
    	            }


    	            url += this.Bex7StrFormat(name, cnt, myvalues );

    	            //in case of data type is date
    	            //For Date:check js date object reference for methods and return types..
    	            //myvalue.getDate() //returns day

    	        }

    	        if(selectionType == 'R')
    	        {
    	            var arrRange = [];
    	            for(var r in ranges)
    	            {
    	                var myrange = ranges[r];
    	                if(type == 'date'){

    	                   var from =  this.dateToString( myrange.from );
    	                   var to =  this.dateToString( myrange.to );
    	                   var operator = myrange.operator;

    	                    myrange = this.Bex7PatchRangeValue(from, to, operator);

    	                    arrRange.push(myrange);
    	                }
    	                else{
    	                    myrange = this.Bex7PatchRangeValue(myrange.from, myrange.to, myrange.operator);
    	                    arrRange.push(myrange);
    	                }
    	                //myrange.operator
    	                //myrange.from
    	                //myrange.to...
    	                //For Date:check js date object reference for methods and return types..

    	            }

    	            url += this.Bex7StrFormat(name, cnt, arrRange.join(';'));

    	        }

    	    }
    	    var fullURL = '';//'/irj/servlet/prt/portal/prtroot/pcd!3aportal_content!2fcom.sap.pct!2fplatform_add_ons!2fcom.sap.ip.bi!2fiViews!2fcom.sap.ip.bi.bex?';
    	    return fullURL + url;
    	},

    	Bex7StrFormat: function (varName, n, values){
    	    var result = "&";
    	    if (varName && values){
    	        result += "BI_COMMAND_"+ n +"-BI_COMMAND_TYPE=SET_VARIABLES_STATE&";
    	        result += "BI_COMMAND_"+ n +"-VARIABLE_VALUES-VARIABLE_VALUE_"+ n +"-VARIABLE_TYPE=VARIABLE_INPUT_STRING&";
    	        result += "BI_COMMAND_"+ n +"-VARIABLE_VALUES-VARIABLE_VALUE_"+ n +"-VARIABLE_TYPE-VARIABLE_INPUT_STRING=";
    	        result += values + "&" + "BI_COMMAND_"+ n +"-VARIABLE_VALUES-VARIABLE_VALUE_"+ n +"-VARIABLE=";
    	        result += varName;

    	    }

    	    return result;
    	},

    	Bex7PatchRangeValue: function (from, to, operator){
    	    var op = operator && operator.toLowerCase();
    	    var result = '';
    	    if (op == 'bt')
    	        result = from + this.Bex7ConvertRangeSigns(operator) + to;
    	    else
    	    	result = this.Bex7ConvertRangeSigns(operator) + from;

    	    return result;
    	},

    	Bex7ConvertRangeSigns: function (rs){

    	    // before accessing rs methond, make sure it is a valid object.
    	    var tmp = rs && rs.toString().toLowerCase();
    	    var signs = {
    	                    "gt":">",
    	                    "ge":">=",
    	                    "lt":"<",
    	                    "le":"<=",
    	                    "bt":" - "
    	                };

    	    var result = signs[tmp];

    	    if(result != undefined)
    	        return result;
    	}
    },

    Util:
    {
        logWindow: undefined,

        timeStamp: function timeStamp()
        {
            var d = new Date();
            return d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + ' - ';
        },

        log: function log(entry)
        {
            var output = this.timeStamp() + ': ' + entry + '\r\n\r\n';
            if(this.logWindow)
            {
                $(this.logWindow).val($(this.logWindow).val() + output );
                var psconsole = $(this.logWindow);
                $(this.logWindow).scrollTop(
                    psconsole[0].scrollHeight - psconsole.height()
                );
            }
            return output;
        },

        getUrlParam: function getUrlParam(name) //returns a url parameter value
        {
            name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
            var regexS = "[\\?&]" + name + "=([^&#]*)";
            var regex = new RegExp(regexS);
            var results = regex.exec(window.location.search);
            if(results == null)
                return "";
            else
                return decodeURIComponent(results[1].replace(/\+/g, " "));
        }
    }
};








/* BOI Singleton Instance ***************************************************************/

/* IMPORTANT: required initialization is done at end of this file */


var BOI = {
    debugMode: true,
    log: BOILib.Util.log('BOI Object Ready.'),
    context: new BOILib.Context(), //Storage of current context
    urlForServer: "sbo.aramco.com.sa",
    urlForOpenDocument : "",
    urlForBex3 : "",
    urlForBex7 : "",
    urlForReportCatalogXML: "",
    urlForReportCatalogJSON: "",
    urlForReportMeta: "",
    urlForVariableF4: "",
    urlForVariableF4Query: "",
    urlForVariableF4Paging: "",
    urlForVariableF4Search: "",

    setLogWindow: function(window) //can be attached to a text area for example to show log messages
    {
        BOILib.Util.logWindow = window;
    },

    loadReportCatalog: function (dashboard, format, successFunction) //uses urlForReportCatalog to return Reports Hierarchy XML/JSON
    {
    	//populates context's navigation reportCatalog attribute xml
        //this function should be called only once. To access the catalog use BOI.context.reportCatalog
    	//execute ajax call

    	var parent = this;
        this.context.parent = this;

        var url = this.urlForReportCatalogXML + dashboard; //for XML Format
        if(format == 'json')
            url = this.urlForReportCatalogJSON + dashboard;

        //always populate context using xml
        if(parent.debugMode) BOI.log += BOILib.Util.log('loadReportCatalog(...), Loading Catalog for ' + dashboard + ' using ' + format);
    	$.ajax({
            url: this.urlForReportCatalogXML + dashboard,
            dataType: "xml",
            success:function(data)
            {
                if(parent.debugMode) BOI.log += BOILib.Util.log('XML Report Catalog Loaded for ' + dashboard + ' using ' + format);
                //on success return the xml and store it in this.context.navigation.reportCatalog
                parent.context.reportCatalog = data;
                parent.context.variables = {};
                if(parent.debugMode) BOI.log += BOILib.Util.log('Start Populating Context');
                parent.context.populateCatalog(); //converts xml into actual report objects
                if(format == BOILib.OutputTypes.XML)
                {
                    if(successFunction)
                        successFunction(); //execute caller desired function for whatever purpose..
                    return data; //returns xml
                }
                else
                {
                    if(parent.debugMode) BOI.log += BOILib.Util.log('Loading JSON Catalog');
                    //ajax call for json format
                    $.ajax({
                        url: parent.urlForReportCatalogJSON + dashboard,
                        dataType: "text", //"jsonp",
                        success:function(data)
                        {
                            if(parent.debugMode) BOI.log += BOILib.Util.log('JSON Catalog Loaded');
                            parent.context.reportCatalogTree = jQuery.parseJSON(data.replace(/isBranch/g,"isFolder"));
                            parent.context.reportCatalogJSON = jQuery.parseJSON(data);
                            //console.log(data);
                            if(successFunction)
                                successFunction(); //execute caller desired function for whatever purpose..
                            return data; //returns json
                        }
                    });
                }
              }
    		});


    },

    getContextReport: function(id) //returns a report object matching the provided id
    {
       return this.context.reports[id];
    },

    loadReportMeta:  function (report, successFunction) //populate report with variables information
    {
    	var parent = this;
        if(parent.debugMode) BOI.log += BOILib.Util.log('BOI.loadReportMeta(...), Loading Variable Information for report ' + report.id + '('+report.tech_name +')');
    	$.ajax({
  		  url: this.urlForReportMeta + report.tech_name, //later put the actual xml + report tech_name
  		  dataType: "xml",
  		  success:function(data)
  		  {
                if(parent.debugMode) BOI.log += BOILib.Util.log('Variables Loaded');
  			    //on success return the xml and store it in for the current report object
  			    report.metadata = data;
  			    var varnames = "";
                var count = 0;
  			    //fill in variables
  			    $(report.metadata).find('ZBUS_VAR_META').each(
  					function()
  					{
  						var variable = new BOILib.Variable();
  						variable.name = $(this).find("ZVARNAME").text(); //ZVARNAME
  						variable.description = $(this).find("ZVAR_DESCRIPTION").text(); //ZVAR_DESCRIPTION
  						variable.type = "string"; //
  						variable.value = $(this).find("DEF_VAL").text(); //DEF_VAL
  						variable.seltype = $(this).find("SEL_TYPE").text(); //DEF_VAL

  						if(variable.name)
  						{
                              count++;
                              if(parent.context.variables[variable.name])
                              {
                                  //already in context
                                  report.variables[variable.name] = parent.context.variables[variable.name];
                              }
                              else
                              {
                                  //add to report
                                  report.variables[variable.name] = variable;
                              }
  						}
  					}
                );
/*
                //TODO: Set Dependencies HERE
                //variable.dependencies[...] = ...

                //TODO:Replace Static info below
                var v = new BOILib.Variable();
                v.name = "DEP_VAR_1";
                v.value = "111";

                for(var i in report.variables)
                    report.variables[i].dependencies[v.name] = v;


                //sample variable whose F4 values come from a query
                var v2 = new BOILib.Variable();
                v2.name = "BW_REP_BASED_VAR_F4_TEST";
                v2.f4query = "YAC_03_HRSD_X_HRROLE_DEP"; //sample
                v2.value = "";
                v2.f4type = BOILib.F4Modes.BWReport; //requires query execution to get f4 VALS


                //TODO end static info
*/
                var exists = 0;
                for(var i in report.variables)
                {
                    if(report.variables[i])
                        if(parent.context.variables[report.variables[i].name] === undefined)
                        {

                            //get F4 values
                            //parent.loadF4Values(report.variables[i]);

                            //then add the variable
                            parent.context.variables[report.variables[i].name] = report.variables[i];

                            //reference to current report
                            parent.context.lastReport = report;
                        }
                }



                if(parent.debugMode) BOI.log += BOILib.Util.log('Done Populating Context with ' + count + ' variables');

                if(successFunction)
                    successFunction(report);
                return report;

  		  }
  		});
    },

    loadF4Values: function(varname, cycle, records, successFunction) //returns paginated variable F4 values
    {
    	//Multiple calls to this function with different cycles will not aggregate the values. Instead,
    	//it will return the last retrieved set only. This is for performance consideration.

    	var parent = this;
    	cycle = typeof cycle !== 'undefined' ? cycle : 1; //use defaults if not specified
    	records = typeof records !== 'undefined' ? records : 100; //use defaults if not specified

    	var cycleSyntax = "&cycle=" + cycle;
    	var recordsSyntax = "&no_record=" + records;

        if(this.context.variables[varname]) //if in context
        {

            if(this.context.variables[varname].f4type == BOILib.F4Modes.BWReport) //require execution
            {
                var v = this.context.variables[varname];
                cycleSyntax = "&page_no=" + cycle;
                recordsSyntax = "&num_records=" + records;
                var varsSyntax = ""; //use for dependency
                var depcount = 0;
                for(var i in v.dependencies)
                    if(v.dependencies[i])
                        depcount++;

                if(depcount > 0)
                    varsSyntax += "&variables=" + BOILib.BexSyntax.getBex3URL("", v.dependencies);

                var url = this.urlForVariableF4Query + v.f4query + varsSyntax + cycleSyntax + recordsSyntax;

                var URI = new BOILib.Uri(url);
                URI.ajaxPost(
                    function(data)
                    {
                        data = $.parseXML(data);
                        var f4set = new BOILib.F4Set();
                        f4set.url = url;
                        f4set.varname = varname;
                        f4set.cycle = cycle;
                        f4set.records = records;
                        f4set.total = $(data).find('TOTAL_REC').text();

                        $(data).find('output item').each(function(){
                            if($(this).children().length < 2)
                            {
                                var f4 = new BOILib.F4();
                                f4.name = $(this).children().first().text();
                                f4.value = f4.name;
                                f4set.values[f4.value] = f4;
                            }
                            else
                            {
                                var f4 = new BOILib.F4();
                                f4.name = $(this).children().first().text();
                                f4.value = $(this).children().last().text();
                                f4set.values[f4.value] = f4;
                            }
                        });
                        if(successFunction)
                            successFunction(f4set); //key/value pairs hashset is inside f4set object's values attribute

                        return f4set;


                    }
                );


                //&page_no=1&num_records=10&paging=X&variables=<BEX3_SYNTAX_FOR_VARS>
                return;
            }
        }

        var f4url = this.urlForVariableF4Paging + varname + cycleSyntax + recordsSyntax;

    	$.ajax({
  		  url: f4url,
  		  dataType: "xml",
          type: 'POST',
  		  success:function(data)
  		  {
            var f4set = new BOILib.F4Set();
                f4set.url = f4url;
                f4set.varname = varname;
                f4set.cycle = cycle;
                f4set.records = records;
                f4set.total = $(data).find('TOTAL_REC').text();

    		$(data).find('IHTTPNVP').each(
    			function()
    			{
    				var f4 = new BOILib.F4();
    				f4.name = $(this).find('NAME').text();
    				f4.value = $(this).find('VALUE').text();
    				f4set.values[f4.value] = f4;
    			}

    		);
            if(successFunction)
                successFunction(f4set); //key/value pairs hashset is inside f4set object's values attribute
    		return f4set;
  		  }
    	});


    },

    loadAllF4Values: function(varname, successFunction) //returns all F4 values for a variable
    {
        if(this.context.variables[varname]) //if in context
            if(this.context.variables[varname].f4type == BOILib.F4Modes.BWReport) //require execution
            {
                var v = this.context.variables[varname];

                var varsSyntax = ""; //use for dependency
                var depcount = 0;
                for(var i in v.dependencies)
                    if(v.dependencies[i])
                        depcount++;

                if(depcount > 0)
                    varsSyntax += "&variables=" + BOILib.BexSyntax.getBex3URL(v.name, v.dependencies);

                var url = this.urlForVariableF4Query + v.f4query + varsSyntax;

                $.ajax({
                    url: url,
                    dataType: "xml",
                    success:function(data)
                    {
                        var f4set = new BOILib.F4Set();
                        f4set.url = url;
                        f4set.varname = varname;
                        f4set.cycle = $(data).find('PAGE_NO').text();
                        f4set.records = $(data).find('RECORD_NO').text();
                        f4set.total = $(data).find('TOTAL_REC').text();

                        $(data).find('OUTPUT item').each(function(){
                            if($(this).children().length < 2)
                            {
                                var f4 = new BOILib.F4();
                                f4.name = $(this).children().first().text();
                                f4.value = f4.name;
                                f4set.values[f4.value] = f4;
                            }
                            else
                            {
                                var f4 = new BOILib.F4();
                                f4.name = $(this).children().first().text();
                                f4.value = $(this).children().last().text();
                                f4set.values[f4.value] = f4;
                            }
                        });
                        if(successFunction)
                            successFunction(f4set); //key/value pairs hashset is inside f4set object's values attribute
                        return f4set;


                    }
                });
                //&page_no=1&num_records=10&paging=X&variables=<BEX3_SYNTAX_FOR_VARS>

            }


        var f4url = this.urlForVariableF4 + varname;

        $.ajax({
            url: f4url,
            dataType: "xml",
            success:function(data)
            {
                var f4set = new BOILib.F4Set();
                f4set.varname = varname;

                $(data).find('IHTTPNVP').each(
                    function()
                    {
                        var f4 = new BOILib.F4();
                        f4.name = $(this).find('NAME').text();
                        f4.value = $(this).find('VALUE').text();
                        f4set.values[f4.name] = f4;
                    }
                );
                if(successFunction)
                    successFunction(f4set); //key/value pairs hashset is inside f4set object's values attribute
                return f4set;
            }
        });
    },

    searchF4Values: function(varname, iobj, term, response) //return variable F4 values containing the term
    {
        //used in autocomplete

        urlForVariableF4Search = urlForVariableF4Search.replace('{varname}', varname).replace('{iobj}', iobj);

        $.ajax({
            url: urlForVariableF4Search + term,
            dataType: "xml",
            success: function( xmlResponse ) {
                response($( "option", xmlResponse ).map(function() {
                    return {
                        value: $( "value", this ).attr('value'),
                        label: $(this).text()
                    };
                }));
            }
        });
    },

    createReport: function(id,type)
    {
        var report = new BOILib.Report();
        report.id = id;
        report.tech_name = id;
        report.type = type;
        switch(type)
        {
            case BOILib.ReportTypes.Bex7:
                report.url = this.urlForBex7;
                break;
            case BOILib.ReportTypes.BO:
            case BOILib.ReportTypes.Crystal:
            case BOILib.ReportTypes.Webi:
            case BOILib.ReportTypes.Xcelsius:
                report.url = 'http://' + this.urlForServer + '/' + this.urlForOpenDocument + id;
                break;
            case BOILib.ReportTypes.Bex3:
                report.url = this.urlForBex3;
                break;
            case BOILib.ReportTypes.Analysis:
            case BOILib.ReportTypes.Explorer:
            case BOILib.ReportTypes.XcelsiusLocal:
            default:
                report.url = id;
                break;
        }

        this.context.reports[report.id] = report;

        return report;
    },

    createContextVariable: function(name, value,  type, seltype, report) //creates a context variable and if already exists, updates it
    {


        if(typeof value != "object")
            value = value + ""; //ensure string format for numbers and booleans

        if (value instanceof Array)
        {
            //alert('value is Array!');
            var varstr = "";
            for(var i in value)
                if(value[i])
                    if(i < value.length - 1)
                        varstr += value[i] + ',';
                    else
                        varstr += value[i];

            value = varstr;
        }

        //all reports viewing state become obsolete due to the new variable value change
        for(var i in this.context.reports)
            this.context.reports[i].state = BOILib.ReportStates.RefreshNeeded;

        var variable = new BOILib.Variable();

        if(this.context.variables[name])
            variable = this.context.variables[name];

        variable.name = name;

        if(type)
            variable.type = type; //date etc
        else
            variable.type = BOILib.VariableTypes.String; //default

        if(seltype)
            variable.seltype = seltype; //M=multi,R=range,S=single
        else
            variable.seltype = BOILib.SelectionTypes.Single; //default

        var isString = value != undefined && value != null && type != BOILib.VariableTypes.Date && type != BOILib.VariableTypes.DateTime;
        //put data in a proper way
        switch(seltype)
        {
        	default:
        	case BOILib.SelectionTypes.Single:
        		variable.value = value;
        		break;
        	case BOILib.SelectionTypes.Multiple:
                variable.values.length = 0;
                if( isString && value.indexOf(',')>-1)
                {
                    variable.values = value.split(','); //supplied values must be separated by commas e.g. 1,2,3
                }
                else
                {
                    if(isString)
                        variable.values.push(value);//supplied values is only one entry
                }

        		break;
        	case BOILib.SelectionTypes.Range:
                var entries = [];
                var ranges = [];
                if( isString && value.indexOf(',')>-1)
                {
                    entries = value.split(','); //supplied value must be an array of range objects e.g. [1..3),(10..12)
                    for(var i in entries)
                    {
                        var range = new BOILib.Range();
                        range.from = entries[i].split('..')[0].substr(1);
                        range.fromMode = entries[i].substr(0,1); //from bracket: ( or [
                        range.to = entries[i].split('..')[1].replace("]","").replace(")","");
                        range.toMode = entries[i].substr(entries[i].length -1,1); //to bracket: ) or ]
                        ranges.push(range);
                    }
                }
                else if(isString) //single range
                {
                    var range = new BOILib.Range();
                    range.from = value.split('..')[0].substr(1);
                    range.fromMode = value.substr(0,1); //from bracket: ( or [
                    range.to = value.split('..')[1].replace("]","").replace(")","");
                    range.toMode = value.substr(value.length -1,1); //to bracket: ) or ]
                    ranges.push(range);
                }
                variable.ranges = ranges;
                break;
        }
        //variable can be associated to an existing report if needed
        if(report)
            report.variables[name] = variable;

        this.context.variables[name] = variable;
        if(parent.debugMode) BOI.log += BOILib.Util.log('BOI.createContextVariable(...), Created Context Variable ' + variable.name + ' ');

        return variable;
    },

    setContextVariable: function(name, value,  type, seltype, report) //sets a value for a context variable; creates the variable if variable does not exist
    {
        return this.createContextVariable(name, value, type, seltype, report);
        //deprecated. use createContextVariable for better control over multiple and range values etc.
       /* if(this.context.variables[name])
        {
            this.context.variables[name].value = value;

            //all reports viewing state become obsolete due to the new variable value change
            for(var i in this.context.reports)
                this.context.reports[i].state = BOILib.ReportStates.RefreshNeeded;
        }
        else
            this.createContextVariable(name, value);

        if(parent.debugMode) BOI.log += BOILib.Util.log('BOI.setContextVariable(...), Variable Value Set for ' + name + ' as ' + value);
        return this.context.variables[name];*/
    },

    getContextVariable: function(varname) //return variable object.
    {
        return this.context.variables[varname];
    },
    openReport: function(id, type, vars, reportVarsOnly, target, refresh) //returns the complete report url (including variable values), type and vars are optional
    {
        refresh = typeof refresh !== 'undefined' ? refresh : true; //use defaults if not specified

        target = typeof target !== 'undefined' ? target : '_blank'; //use defaults if not specified

        var parent = this;
        var report; //current report
        var url = ""; //output
        var varsSet = {};

        if(parent.debugMode) BOI.log += BOILib.Util.log('BOI.openReport(...), Opening Report ' + id + ' of type ' + type + ' into target ' + target);

        if (typeof vars == 'string' || vars instanceof String)
        {
            //if vars is a string, it must be of this form (OpenDocumentSyntax):
            //&lsSVAR_1=9999&lsMVAR_2=aa;bb;cc&lsRVAR_3=[2000..2008)&lsSMyDate=Date(2010,11,16)&lsMDates=Date(2000,5,19);Date(2012,12,20)&lsRDateRange=[Date(2010,3,8)..Date(2005,9,23)]&...

            //reverse parsing of opendocument syntax into javascript objects

            //breakdown by ampersand (&)
            var params = vars.split('&');

            var tempVar;

            for(var p in params)
            {
                if(params[p].indexOf('=') > -1)
                {
                    //break each by (=)
                    var typename = params[p].split('=')[0];
                    var wholevalue = params[p].split('=')[1];
                    var pname = typename.substr(3);
                    var ptype = typename.substr(0,3); //selection type
                    var vtype = wholevalue.indexOf('Date(') == 0 ? BOILib.VariableTypes.Date : BOILib.VariableTypes.String;
                    var pvalue = wholevalue;
                    //Determine the type lsS,lsM, or lsR
                    switch(ptype)
                    {
                        //if lsS
                        case "lsS":
                            //Determine the value datatype: if it contains "Date(" then it is a date variable, otherwise it is a string
                            if(wholevalue.indexOf('Date(') == 0)
                            {
                                //if date, get the value and convert it into JS Date
                                var dateCommaSeparated = wholevalue.replace('Date(','').replace(')',''); //results in yyyy,m,d
                                var parts = dateCommaSeparated.split(',');
                                pvalue = new Date(parts[0],parts[1],parts[2]);
                                vtype = BOILib.VariableTypes.Date;
                            }
                            tempVar = this.createContextVariable(pname,pvalue,vtype,BOILib.SelectionTypes.Single);
                            break;
                        //if lsM,
                        case "lsM":
                            //breakdown values by (;)
                            var entries = pvalue.split(';');
                            tempVar = this.createContextVariable(pname,null,vtype,BOILib.SelectionTypes.Multiple);
                            //for each value
                            for(var v in entries)
                            {
                                var val = entries[v];
                                //Determine the value datatype: if it contains "Date(" then it is a date variable, otherwise it is a string
                                if(val.indexOf('Date(') == 0)
                                {
                                    //if date, get the value and convert it into JS Date
                                    var dateCommaSeparated = val.replace('Date(','').replace(')',''); //results in yyyy,m,d
                                    var parts = dateCommaSeparated.split(',');
                                    val = new Date(parts[0],parts[1],parts[2]);
                                }
                                tempVar.values.push(val);
                            }
                            break;
                        //if lsR, breakdown by (..)
                        case "lsR":
                            //pull out the enclosures
                            var encStart = pvalue.substr(0,1); //first bracket
                            var encClose = pvalue.substr(pvalue.length-1,1); //last bracket

                            var from = pvalue.split('..')[0].substr(1);
                            var to = pvalue.split('..')[1];
                                to = to.substr(0,to.length-1);

                            //Determine the value datatype: if it contains "Date(" then it is a date variable, otherwise it is a string
                            if(from.indexOf('Date(') == 0)
                            {
                                vtype = BOILib.VariableTypes.Date;
                                //if date, get the value and convert it into JS Date
                                var dateCommaSeparated = from.replace('Date(','').replace(')',''); //results in yyyy,m,d
                                var parts = dateCommaSeparated.split(',');
                                from = new Date(parts[0],parts[1],parts[2]);
                            }
                            if(to.indexOf('Date(') == 0)
                            {
                                //if date, get the value and convert it into JS Date
                                var dateCommaSeparated = to.replace('Date(','').replace(')',''); //results in yyyy,m,d
                                var parts = dateCommaSeparated.split(',');
                                to = new Date(parts[0],parts[1],parts[2]);
                            }
                            var val = new BOILib.Range(encStart,from, to, encClose);
                            tempVar = this.createContextVariable(pname,null,vtype,BOILib.SelectionTypes.Range);
                            tempVar.ranges.push(val);
                            break;
                    }

                    varsSet[tempVar.name] = tempVar;
                }
            }


        }
        else if(vars) //vars must be a hashset
    	{
            varsSet = vars;
            var varcount = 0;
    		for(var i in vars) //i is the varname here
            {
                varcount++;
            	if(this.context.variables[i]) //if already in context, set its value
    				this.context.variables[i].value = vars[i];
    			else
    			{
    				var newvar = new BOILib.Variable();
    				newvar.name = i;
    				newvar.value = vars[i];

    				this.context.variables[i] = newvar;
    			}
            }
            if(varcount>0) //all reports will have obsolete viewstate due to being affected by new variables values
                for(var i in parent.context.reports)
                    parent.context.reports[i].state = BOILib.ReportStates.RefreshNeeded;

            if(parent.debugMode) BOI.log += BOILib.Util.log('Parsed Variables ' + varcount);
    	}



    	//get report object from catalog
    	if(this.context.reports[id]) //report exist in context
    	{
            if(refresh)
                this.context.reports[id].state = BOILib.ReportStates.RefreshNeeded;
            else
                this.context.reports[id].state = BOILib.ReportStates.Current;

            if(parent.debugMode) BOI.log += BOILib.Util.log('Report Exists in Context ' + this.context.reports[id].id + ', ' + this.context.reports[id].tech_name);
    		//register vars in report
            if(varsSet) //vars must be a hashset
        	{
        		for(var i in varsSet) //i is the varname here
        			this.context.reports[id].variables[i] = this.context.variables[i];
        	}
    		if(reportVarsOnly)
    		{	//return this.context.generateReportUri(report, true).url;
    			url = this.context.generateReportUri(this.context.reports[id], true).post(target);
    		}
    		else
    		{	//return this.context.generateReportUri(report).url;
    			url = this.context.generateReportUri(this.context.reports[id]).post(target);
    		}
            if(parent.debugMode) BOI.log += BOILib.Util.log('Use Report Variables Only = '+ reportVarsOnly+', Report URL is ' + url);

            this.context.reports[id].state = BOILib.ReportStates.Current; //report viewstate is now up to date

            return url;
    	}
    	else //report does not exist in context
    	{
            if(parent.debugMode) BOI.log += BOILib.Util.log('Report Does Not Exist in Context ');
    		//new report
    		report = new BOILib.Report();
    		report.id = id;
    		report.tech_name = id;
    		report.type = type;
    		report.url = 'http://' + parent.urlForServer + '/' + parent.urlForOpenDocument + id; //TODO: must be retrieved..

            if(refresh)
                report.state = BOILib.ReportStates.RefreshNeeded;
            else
                report.state = BOILib.ReportStates.Current;

    		//register vars under the new report
            var vcount = 0;
    		if(varsSet) //vars must be a hashset
        	{
        		for(var i in varsSet) //i is the varname here
                {
                    vcount++;
                    report.variables[i] = this.context.variables[i];
                }
        	}
            if(parent.debugMode) BOI.log += BOILib.Util.log('Report Created in Context ' + report.id + ' with ' + vcount +' New Variables, URL: ' + report.url);
    		//add report to context
    		this.context.reports[id] = report;

    		//get report variables and insert the vars into context
    		this.loadReportMeta(parent.context.reports[id],
    				function ()
    				{
    					if(reportVarsOnly)
    					{	//return this.context.generateReportUri(report, true);
    						url = parent.context.generateReportUri(parent.context.reports[id], true).post(target);
                            if(parent.debugMode) BOI.log += BOILib.Util.log('Generated POST Report URL: ' + url);
                            report.state = BOILib.ReportStates.Current;
    						return url;
    					}
    					else
    					{	//return this.context.generateReportUri(report);
    						url = parent.context.generateReportUri(parent.context.reports[id]).post(target);
                            if(parent.debugMode) BOI.log += BOILib.Util.log('Generated POST Report URL: ' + url);
                            report.state = BOILib.ReportStates.Current;
                            return url;
    					}
    				});
    		
    	}
    		
    }




};




/** BOI initialization **/
$(function()
	{
	    /* Required Dynamic iFrame */
	    $('<iframe id="boi_open_report" name="boi_open_report" style="display:none;">').appendTo('body');
        BOI.context.parent = BOI;

        //initial values in case unable to communicate
        BOI.urlForServer = "dj1.aramco.com.sa";
        BOI.urlForOpenDocument = "BOE/OpenDocument/opendoc/openDocument.jsp?sIDType=CUID&iDocID=";
        BOI.urlForVariableF4Search = "../zbw_reporting/var_search.htm?var_name={varname}&info_object={iobj}&infocube=ZMDOCOVER&read_mode=M&search_term=";

        //Report Catalog - XML
        BOI.urlForReportCatalogXML = "../zbw_reporting/report_hier.do?view_name=dbd_rep_tree%2exml&template_id=";
        //Report Catalog - JSON
        BOI.urlForReportCatalogJSON = "../zbw_reporting/report_hier.do?view_name=dbd_rep_tree.JSON&template_id=";
        //Report variables information
        BOI.urlForReportMeta = "../zbw_reporting/variable_screen_ext.xml?rep_id=";
        //Variable F4 Values
        BOI.urlForVariableF4Paging = "../ZBW_REPORTING/variable_values_paging.xml?read_mode=C&page_flag=X&var_name=";
        BOI.urlForVariableF4 = "../ZBW_REPORTING/variable_values.xml?read_mode=C&var_name=";
        BOI.urlForVariableF4Query = "../zbw_reporting/execute_report_oo.htm?paging=X&query=";//sample query: YAC_03_HRSD_X_HRROLE_DEP
                                    //append &page_no=1&num_records=10&paging=X&variables=<BEX3_SYNTAX_FOR_VARS>

        //bex7
        BOI.urlForBex7 = "http://dvb.aramco.com.sa:52000/irj/servlet/prt/portal/prtroot/pcd!3aportal_content!2fcom.sap.pct!2fplatform_add_ons!2fcom.sap.ip.bi!2fiViews!2fcom.sap.ip.bi.bex?";
        //bex3
        BOI.urlForBex3 = "http://dvb.aramco.com.sa:1080/sap/bw/BEx?variable_screen=&query=";

        //var boi_urlForInit = "api/data/get_parameter_xml.xml"; //COMMENT THIS WHEN GOING TO ONLINE MODE
		var boi_urlForInit = "../zbw_reporting/get_parameter_xml.xml"; //UNCOMMENT TO GO ONLINE
		$.ajax({
		    url: boi_urlForInit,
		    dataType: "xml",
		    success:function(data)
		    {
                BOI.log+=BOILib.Util.log('BOI Initializing');

                BOI.urlForServer = $(data).find("param[name='bo_server']").attr("value");//"http://[[[[[[[[dj1.aramco.com.sa]]]]]]]]/",

                BOI.urlForBex7 = $(data).find("param[name='prt_protcl']").attr("value") +
                                 "://" +
                                 $(data).find("param[name='prt_server']").attr("value") +
                                 "/" +
                                 $(data).find("param[name='BI_LAUNCHER']").attr("value") +
                                 "?";

                BOI.urlForBex3 = $(data).find("param[name='prt_protcl']").attr("value") +
                                "://" +
                                $(data).find("param[name='BW_SERVER']").attr("value") +
                                "/" +
                                "sap/bw/BEx?variable_screen=&query=";

                // COMMENT THIS WHEN GOING TO ONLINE MODE
                // AS THESE ARE USED FOR OFFLINE TESTING ONLY
/*
                BOI.urlForReportCatalogXML = "api/data/report_hier.xml?id=";
                BOI.urlForReportCatalogJSON = "api/data/report_hier.json?id=";
                BOI.urlForReportMeta = "api/data/variable_screen_ext.xml?id=";
                BOI.urlForVariableF4Paging = "api/data/variable_values.xml?id=";

                BOI.log+=BOILib.Util.log('BOI Initialized');
                return;*/
                //--------------------------------------------------


			       
			    //F4 Values to provide the name value pairs for the variable values
			    	//cycle=1 means the current page to begin with (defaulted here to first page)
			    	//no_record means total number of records to show in the page (defaulted here to 100)
				BOI.log+=BOILib.Util.log('BOI Initialized');
		    }
	   });
	}	
);


